﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Sustainalytics.Synchronization.Interface;
using MongoDB.Driver;
using Sustainalytics.Entities.RiskRatings;
using MongoDB.Bson;
using System.Collections.Concurrent;
using System.Threading;

namespace Sustainalytics.ClientDW.Synchronization.RiskRatingsBenchmarks
{
    public class RiskRatingsHistoricalDataSync
    {
        private readonly ILogger _logger;
        private readonly IMongoCollection<RiskRatingResearchProfile> _researchProfileCollection;
        private readonly IMongoCollection<RiskRatingHistoricalDataSet> _historicalDataSetCollection;
        private readonly IMongoCollection<RiskRatingHistoricalData> _historicalDataCollection;
        private readonly IMongoCollection<RiskRatingBenchmark> _benchmarkCollection;


        public RiskRatingsHistoricalDataSync(ILogger logger,
            IMongoCollection<RiskRatingResearchProfile> researchProfileCollection,
            IMongoCollection<RiskRatingHistoricalDataSet> historicalDataSetCollection,
            IMongoCollection<RiskRatingHistoricalData> historicalDataCollection,
            IMongoCollection<RiskRatingBenchmark> benchmarkCollection)
        {
            _logger = logger;
            _researchProfileCollection = researchProfileCollection;
            _historicalDataSetCollection = historicalDataSetCollection;
            _historicalDataCollection = historicalDataCollection;
            _benchmarkCollection = benchmarkCollection;
        }

        public void Start(CancellationToken cancellationToken)
        {
            _logger.Log("Begin Risk Ratings Historycal data sync step");


            if (!ShouldExecuteSync())
            {
                _logger.Log("Historycal data sync step processing 0 entries");
                return;
            }


            var historicalDataSet = GetHistoricalDataSet();

            _logger.Log(string.Format("Historycal data is processed for date: {0}", historicalDataSet.CreationDate));

            var historicalData = GetHistoricalData(cancellationToken, historicalDataSet);

            SaveHistoricalData(historicalDataSet, historicalData);

        }

        public bool ShouldExecuteSync()
        {
            var lastHistoricalDataSet = GetLastHistoricalDataSet();


            if (lastHistoricalDataSet == null)
            {
                return true;
            }

            if (GetFirstDayOfMonth(lastHistoricalDataSet.CreationDate.Date) < GetFirstDayOfMonth(DateTime.Today.Date))
            {
                return true;
            }

            if (!lastHistoricalDataSet.ToBePreserved)
            {
                _historicalDataSetCollection.DeleteOne(x => x.CreationDate == lastHistoricalDataSet.CreationDate);
                _historicalDataCollection.DeleteMany(x => x.Date == GetFirstDayOfMonth(lastHistoricalDataSet.CreationDate.Date));

                return true;
            }

            return false;
        }


        private RiskRatingHistoricalDataSet GetLastHistoricalDataSet()
        {
            var historicalDataSet = _historicalDataSetCollection.Find(new BsonDocument())
                .ToEnumerable()
                .OrderByDescending(x => x.CreationDate)
                .FirstOrDefault();

            return historicalDataSet;
        }

        private DateTime GetFirstDayOfMonth(DateTime date)
        {
            return new DateTime(date.Year, date.Month, 1, 0, 0, 0, 0, DateTimeKind.Utc);
        }

        private RiskRatingHistoricalDataSet GetHistoricalDataSet()
        {
            var syncDate = DateTime.Today.Date;
            var storeDate = GetFirstDayOfMonth(DateTime.Today.Date);

            return new RiskRatingHistoricalDataSet()
            {
                Id = Guid.NewGuid(),
                SetName = $"set_{storeDate.ToString("d")}",
                CreationDate = syncDate,
                ToBePreserved = true
            };

        }


        private List<RiskRatingHistoricalData> GetHistoricalData(CancellationToken cancellationToken, RiskRatingHistoricalDataSet historicalDataSet)
        {

            var options = new ParallelOptions
            {
                CancellationToken = cancellationToken,
                MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling(Environment.ProcessorCount * 0.75))
            };

            var researchProfiles = _researchProfileCollection.Find(new BsonDocument()).ToList();


            var benchmarkDictionary = _benchmarkCollection.Find(new BsonDocument())
                .ToEnumerable()
                .ToDictionary(k => k.Id);


           var results = new ConcurrentBag<RiskRatingHistoricalData>();

            Parallel.ForEach(researchProfiles, options, researchProfile =>
            {
                var universePercentile = benchmarkDictionary.ContainsKey(researchProfile.Id)
                    ? benchmarkDictionary[researchProfile.Id].CompanyBenchmark.UniversePerformanceRisk.Percentile
                    : -1;

                var historicalData = new RiskRatingHistoricalData
                {
                    Id = Guid.NewGuid(),
                    Date = GetFirstDayOfMonth(historicalDataSet.CreationDate.Date),
                    CompanyId = researchProfile.Id,

                    UnmanagedRisk = researchProfile.ManagementRisk.UnmanagedRisk.Value,
                    ManagedRisk = researchProfile.ManagementRisk.ManagedRisk.Value,
                    Exposure = researchProfile.ManagementRisk.Exposure.Value,
                    Management = researchProfile.ManagementRisk.Management.Value,
                    UniversePercentile = universePercentile,

                    MaterialEsgIssues = new List<RiskRatingMaterialEsgIssueBaseHistoricalData>()
                };

                foreach(var mei in researchProfile.MaterialEsgIssues)
                {
                    historicalData.MaterialEsgIssues.Add(new RiskRatingMaterialEsgIssueBaseHistoricalData()
                    {
                        ExternalId = mei.ExternalId,
                        MeiCode = mei.MeiCode,

                        UnmanagedRisk = mei.ManagementRisk.UnmanagedRisk.Value,
                        ManagedRisk = mei.ManagementRisk.ManagedRisk.Value,
                        Exposure = mei.ManagementRisk.Exposure.Value,
                        Management = mei.ManagementRisk.Management.Value
                    });
                }

                results.Add(historicalData);
            });

            return results.ToList();
        }

        private void SaveHistoricalData(RiskRatingHistoricalDataSet historicalDataSet, List<RiskRatingHistoricalData> historicalData)
        {
            _historicalDataCollection.InsertMany(historicalData);
            _historicalDataSetCollection.InsertOne(historicalDataSet);
        }

    }
}